"use strict";

const utils = require("./../utils");
const settle = require("./../core/settle");
const buildURL = require("./../helpers/buildURL");
const buildFullPath = require("../core/buildFullPath");
const parseHeaders = require("./../helpers/parseHeaders");
const isURLSameOrigin = require("./../helpers/isURLSameOrigin");
const createError = require("../core/createError");

/**
 * 使用Promise包装xhr请求
 *
 * @param {Object} axios配置
 */
module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    const requestData = config.data;
    const requestHeaders = config.headers;

    if (utils.isFormData(requestData)) {
      delete requestHeaders["Content-Type"]; // 让浏览器进行设置
    }

    let request = new XMLHttpRequest();

    // HTTP 基本身份验证
    if (config.auth) {
      const username = config.auth.username || "";
      const password = config.auth.password || "";
      requestHeaders.Authorization = "Basic " + btoa(username + ":" + password);
    }

    const fullPath = buildFullPath(config.baseURL, config.url);
    request.open(
      config.method.toUpperCase(),
      buildURL(fullPath, config.params, config.paramsSerializer),
      true
    );

    // 设置请求超时时间
    request.timeout = config.timeout;

    // 监听网络请求是否就绪
    request.onreadystatechange = function handleLoad() {
      if (!request || request.readyState !== 4) {
        return;
      }

      //请求出错和我们没有得到响应，这将由onerror处理，
      //除了一个例外：当请求使用file协议时，大多数浏览器即使成功请求也将返回0这个状态
      if (
        request.status === 0 &&
        !(request.responseURL && request.responseURL.indexOf("file:") === 0)
      ) {
        return;
      }

      // 准备响应
      const responseHeaders =
        "getAllResponseHeaders" in request
          ? parseHeaders(request.getAllResponseHeaders())
          : null;
      const responseData =
        !config.responseType || config.responseType === "text"
          ? request.responseText
          : request.response;
      const response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request,
      };

      settle(resolve, reject, response);

      // 清理请求
      request = null;
    };

    // 处理浏览器请求取消的情况（与手动取消相反）
    request.onabort = function handleAbort() {
      if (!request) {
        return;
      }

      reject(createError("Request aborted", config, "ECONNABORTED", request));

      // 清理请求
      request = null;
    };

    // 处理底层网络错误
    request.onerror = function handleError() {
      //浏览器对我们隐藏了真正的错误onerror仅在网络错误时才应触发
      reject(createError("Network Error", config, null, request));

      // 清理请求
      request = null;
    };

    // 处理超时
    request.ontimeout = function handleTimeout() {
      let timeoutErrorMessage = "timeout of " + config.timeout + "ms exceeded";
      if (config.timeoutErrorMessage) {
        timeoutErrorMessage = config.timeoutErrorMessage;
      }
      reject(createError(timeoutErrorMessage, config, "ECONNABORTED", request));

      // 清理请求
      request = null;
    };

    //添加xsrf标头
    //仅在标准浏览器环境中运行时才能执行此操作。
    //特别是如果我们是web worker或react-native，则不会。
    if (utils.isStandardBrowserEnv()) {
      const cookies = require("./../helpers/cookies");

      //添加xsrf标头
      const xsrfValue =
        (config.withCredentials || isURLSameOrigin(fullPath)) &&
        config.xsrfCookieName
          ? cookies.read(config.xsrfCookieName)
          : undefined;

      if (xsrfValue) {
        requestHeaders[config.xsrfHeaderName] = xsrfValue;
      }
    }

    // 添加请求头
    if ("setRequestHeader" in request) {
      utils.forEach(requestHeaders, function setRequestHeader(val, key) {
        if (
          typeof requestData === "undefined" &&
          key.toLowerCase() === "content-type"
        ) {
          // 如果数据未定义，则删除Content-Type
          delete requestHeaders[key];
        } else {
          // 否则添加请求头
          request.setRequestHeader(key, val);
        }
      });
    }

    // 添加withCredentials进行请求（如果需要）
    if (!utils.isUndefined(config.withCredentials)) {
      request.withCredentials = !!config.withCredentials;
    }

    // 如果需要，将responseType添加到请求
    if (config.responseType) {
      try {
        request.responseType = config.responseType;
      } catch (e) {
        //浏览器抛出的预期DOMException不兼容XMLHttpRequest Level 2。
        //但是，对于'json'类型，可以取消此设置，因为默认情况下'transformResponse'函数可以对其进行解析。
        if (config.responseType !== "json") {
          throw e;
        }
      }
    }

    // 根据需要处理进度
    if (typeof config.onDownloadProgress === "function") {
      request.addEventListener("progress", config.onDownloadProgress);
    }

    // 并非所有浏览器都支持上传事件
    if (typeof config.onUploadProgress === "function" && request.upload) {
      request.upload.addEventListener("progress", config.onUploadProgress);
    }

    if (config.cancelToken) {
      // 处理取消
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();
        reject(cancel);
        // 清理请求
        request = null;
      });
    }

    if (requestData === undefined) {
      requestData = null;
    }

    // 发送请求
    request.send(requestData);
  });
};
